// ST-011 SNES DSP adapted from Morita Shogi 64
//
// notes:
// - the SNES uses DMA to/from 60:0000 and maybe 68:0xxx
// - some code redundancy (helper subroutines for movement projection)
//
// - OPS04/05 have unknown output values (!)
// - OPS06/07 have unknown purposes
//
// - plenty of missing opcodes that don't show up in the only known binary log (st011-demo)
//   (play the game until captured/promoted pieces, king checked, endgame)
// - minus emulation cycles (busy signals), bit-perfect to 'st011-demo'

//#define DEBUG_DSP

#ifdef DEBUG_DSP
#include <stdio.h>
int debug1, debug2;
int line_count;
#endif


void (*RunST011)();
void ST011_Command();

unsigned char ST011_DR;
unsigned char ST011_SR;

int ST011_input_length;

#define ST011_ram setaramdata

extern unsigned char *setaramdata;

#define ST011_board ( ST011_ram+0x130 )

int ST011_dma_count;
int ST011_dma_index;

int ST011_king1;
int ST011_king2;

// (x,y)
#define MOVE_UUL   -1,-20
#define MOVE_UL		 -1,-10
#define MOVE_ULAll -9,- 9
#define MOVE_U		  0,-10
#define MOVE_UAll   0,- 9
#define MOVE_UR		  1,-10
#define MOVE_URAll  9,- 9
#define MOVE_UUR    1,-20

#define MOVE_L		 -1,  0
#define MOVE_LAll	 -9,  0
#define MOVE_R		  1,  0
#define MOVE_RAll	  9,  0

#define MOVE_DDL	 -1, 20
#define MOVE_DL		 -1, 10
#define MOVE_DLAll -9,  9
#define MOVE_D		  0, 10
#define MOVE_DAll   0,  9
#define MOVE_DR		  1, 10
#define MOVE_DRAll  9,  9
#define MOVE_DDR	  1, 20

#define MOVE_STOP 127,127
#define MOVE_NOP	  0,  0
const int ST011_move_table[8*2][9*2] =
{
	// Pawn: one step forward
	// - Promoted: same as Gold
	{ MOVE_D, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_U, MOVE_STOP, MOVE_NOP, MOVE_NOP },

	// Lance: all steps forward
	// - Promoted: same as Gold
	{ MOVE_DAll, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_U, MOVE_STOP, MOVE_NOP, MOVE_NOP },

	// Knight: one step side, two forward
	// - Promoted: same as Gold
	{ MOVE_DDR, MOVE_DDL, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_U, MOVE_STOP, MOVE_NOP, MOVE_NOP },

	// Silver general: one any diagonal, one step forward
	// - Promoted: same as Gold
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_UR, MOVE_UL, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_U, MOVE_STOP, MOVE_NOP, MOVE_NOP },

	// Gold general: one any forward, one sideways or one backward
	// - Promoted: N/A
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_U, MOVE_STOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },

	// Bishop: any diagonal
	// - Promoted: Bishop + King
	{ MOVE_DRAll, MOVE_DLAll, MOVE_URAll, MOVE_ULAll, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DRAll, MOVE_D, MOVE_DLAll, MOVE_R, MOVE_L, MOVE_URAll, MOVE_U, MOVE_ULAll, MOVE_STOP },

	// Rook: any vertical, horizontal
	// - Promoted: Rook + King
	{ MOVE_DAll, MOVE_RAll, MOVE_LAll, MOVE_UAll, MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
	{ MOVE_DR, MOVE_DAll, MOVE_DL, MOVE_RAll, MOVE_LAll, MOVE_UR, MOVE_UAll, MOVE_UL, MOVE_STOP },

	// King: one any direction
	// - Promoted: N/A
	{ MOVE_DR, MOVE_D, MOVE_DL, MOVE_R, MOVE_L, MOVE_UR, MOVE_U, MOVE_UL, MOVE_STOP },
	{ MOVE_STOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP, MOVE_NOP },
};


void ST011_Reset()
{
  RunST011 = &ST011_Command;
  ST011_SR = 0xc4;
}


void ST011_OP01_A()
{
	if( ST011_dma_count-- )
	{
		ST011_board[ ST011_dma_index++ ] = ST011_DR;
	}

	if( ST011_dma_count == 0 )
	{
#ifdef DEBUG_DSP
		int lcv1, lcv2;
#endif
		int lcv;

		for( lcv = 0; lcv < 11; lcv++ )
		{
			ST011_board[ lcv ] = 0;
		}
		for( lcv = 11; lcv < 21; lcv++ )
		{
			ST011_board[ lcv ] = 0x80;
		}

		ST011_king1 = ST011_board[ 126+21 ];
		ST011_king2 = ST011_board[ 127+21 ];

		RunST011 = &ST011_Command;
		ST011_SR = 0xc4;

#ifdef DEBUG_DSP
		// Debug
		printf( "OP01\n" );
		for( lcv1 = 0; lcv1 < 9; lcv1++ )
		{
			for( lcv2 = 0; lcv2 < 10; lcv2++ )
			{
				printf( "%02x ", ST011_board[ lcv1*10 + lcv2 + 21 ] );
			}

			printf( "\n" );
		}
		printf( "OP01 END\n\n" );
#endif
	}
}

void ST011_OP01()
{
	ST011_dma_count = 128;
	ST011_dma_index = 0+21;

	RunST011 = &ST011_OP01_A;
	ST011_SR = 0xa4;
}


void ST011_OP02_A()
{
	if( ST011_dma_count-- )
	{
		ST011_DR = ST011_ram[ ST011_dma_index-- ];
	}

	if( ST011_dma_count == 0 )
	{
#ifdef DEBUG_DSP
		int lcv1, lcv2;
#endif

		RunST011 = &ST011_Command;
		ST011_SR = 0xc4;

#ifdef DEBUG_DSP
		// Debug
#define OP02_ROW 10

		printf( "OP02\n" );
		for( lcv1 = 0; lcv1 < 0x83 / OP02_ROW; lcv1++ )
		{
			for( lcv2 = 0; lcv2 < OP02_ROW; lcv2++ )
			{
				printf( "%02x ", ST011_ram[ debug1 - lcv1 * OP02_ROW - lcv2 ] );
			}

			printf( "\n" );
		}
		printf( "OP02 END\n\n" );
#endif
	}
}

void ST011_OP02()
{
	switch( ST011_input_length-- )
	{
		case 4: ST011_dma_index = ST011_DR;	break;
		case 3: ST011_dma_index |= ST011_DR << 8;	break;
		case 2: ST011_dma_count = ST011_DR;	break;
		case 1:
			ST011_dma_count |= ST011_DR << 8;

#ifdef DEBUG_DSP
			debug1 = ST011_dma_index;
			debug2 = 0;
#endif

			RunST011 = &ST011_OP02_A;
			ST011_SR = 0xa4;
			break;
	}
}


void ST011_Project_Moves( int color )
{
	int row, col, lcv, index;
	int dir;

	index = 0x121;
	for( lcv = 0; lcv < 0x83; lcv++ )
	{
		ST011_ram[ index-- ] = 0;
	}
	index = 0x121 - 21;

	if( color == 0x20 )
	{
		dir = 1;
	}
	else
	{
		dir = -1;
	}

	for( row = 0; row < 9; row++ )
	{
		for( col = 0; col < 10; col++ )
		{
			int shogi_piece;
			int piece_id;
			int lcv_steps, lcv_move;
			int move_list[ 9*2 ];

			shogi_piece = ST011_board[ row*10+col+21 ];
			piece_id = shogi_piece & 0x1f;

			if( col == 9 ) continue;
			if( shogi_piece == 0x00 ) continue;
			if( ( shogi_piece & ~0x1f ) != color ) continue;

			for( lcv = 0; lcv < 9*2; lcv++ )
			{
				move_list[ lcv ] = ST011_move_table[ piece_id >> 1 ][ lcv ];
			}

			lcv_move = 0;
			while( move_list[ lcv_move ] != 0x7f )
			{
				int pos_x, pos_y;

				lcv_steps = 1;
				if( move_list[ lcv_move ] == 9 || move_list[ lcv_move ] == -9 )
				{
					lcv_steps = 9;
					if( move_list[ lcv_move ] == 9 )
					{
						move_list[ lcv_move ] = 1;
					}
					else
					{
						move_list[ lcv_move ] = -1;
					}
				}

				if( move_list[ lcv_move+1 ] == 9 || move_list[ lcv_move+1 ] == -9 )
				{
					lcv_steps = 9;
					if( move_list[ lcv_move+1 ] == 9 )
					{
						move_list[ lcv_move+1 ] = 1;
					}
					else
					{
						move_list[ lcv_move+1 ] = -1;
					}
				}
				else
				{
					move_list[ lcv_move+1 ] /= 10;
				}

				pos_x = col;
				pos_y = row;
				while( lcv_steps-- )
				{
					pos_x += move_list[ lcv_move+0 ];
					pos_y += ( move_list[ lcv_move+1 ] * dir );

					ST011_ram[ index - pos_y*10 - pos_x ] = 0x80;

					if( ST011_board[ pos_y*10 + pos_x + 21 ] ) break;
				}

				lcv_move += 2;
			}
		} // end col
	} // end row
}


int ST011_Project_Valid_Moves( int color )
{
	int row, col, lcv, index;
	int dir;

	index = 0x556;

	if( color == 0x20 )
	{
		dir = 1;
	}
	else
	{
		dir = -1;
	}

	for( row = 0; row < 9; row++ )
	{
		for( col = 0; col < 10; col++ )
		{
			int shogi_piece;
			int piece_id;
			int lcv_steps, lcv_move;
			int move_list[ 9*2 ];

			shogi_piece = ST011_board[ row*10+col+21 ];
			piece_id = shogi_piece & 0x1f;

			if( col == 9 ) continue;
			if( shogi_piece == 0x00 ) continue;
			if( ( shogi_piece & ~0x1f ) != color ) continue;

			for( lcv = 0; lcv < 9*2; lcv++ )
			{
				move_list[ lcv ] = ST011_move_table[ piece_id >> 1 ][ lcv ];
			}

			lcv_move = 0;
			while( move_list[ lcv_move ] != 0x7f )
			{
				int pos_x, pos_y;

				lcv_steps = 1;
				if( move_list[ lcv_move ] == 9 || move_list[ lcv_move ] == -9 )
				{
					lcv_steps = 9;
					if( move_list[ lcv_move ] == 9 )
					{
						move_list[ lcv_move ] = 1;
					}
					else
					{
						move_list[ lcv_move ] = -1;
					}
				}

				if( move_list[ lcv_move+1 ] == 9 || move_list[ lcv_move+1 ] == -9 )
				{
					lcv_steps = 9;
					if( move_list[ lcv_move+1 ] == 9 )
					{
						move_list[ lcv_move+1 ] = 1;
					}
					else
					{
						move_list[ lcv_move+1 ] = -1;
					}
				}
				else
				{
					move_list[ lcv_move+1 ] /= 10;
				}

				pos_x = col;
				pos_y = row;
				while( lcv_steps-- )
				{
					pos_x += move_list[ lcv_move+0 ];
					pos_y += ( move_list[ lcv_move+1 ] * dir );

					if( pos_x < 0 ) break;
					if( pos_x > 8 ) break;
					if( pos_y < 0 ) break;
					if( pos_y > 8 ) break;
					if( ( ST011_board[ pos_y*10 + pos_x + 21 ] & ~0x1f ) == color ) break;

					ST011_ram[ index + 0x000 ] = 21 + row*10 + col;
					ST011_ram[ index + 0x001 ] = 0;
					ST011_ram[ index + 0x418 ] = 21 + pos_y*10 + pos_x;
					ST011_ram[ index + 0x419 ] = 0;

					if( pos_y >= 6 )
					{
						ST011_ram[ index + 0x418 ] |= 0x80;
					}
					index += 2;

					if( ST011_board[ pos_y*10+pos_x+21 ] ) break;
				}

				lcv_move += 2;
			}
		} // end col
	} // end row

	return ( index-0x556 ) >> 1;
}


void ST011_OP04()
{
	ST011_Project_Moves( 0x40 );

	// unknown outputs
	ST011_ram[ 0x12c ] = 0;
	ST011_ram[ 0x12d ] = 0;
	ST011_ram[ 0x12e ] = 0;
	ST011_ram[ 0x12f ] = 0;

	RunST011 = &ST011_Command;
	ST011_SR = 0xc4;
}


void ST011_OP05()
{
	ST011_Project_Moves( 0x20 );

	// unknown outputs
	ST011_ram[ 0x12c ] = 0;
	ST011_ram[ 0x12d ] = 0;
	ST011_ram[ 0x12e ] = 0;
	ST011_ram[ 0x12f ] = 0;

	RunST011 = &ST011_Command;
	ST011_SR = 0xc4;
}


void ST011_OP0E()
{
	int valid_moves;

	valid_moves = ST011_Project_Valid_Moves( 0x20 );

	ST011_ram[ 0x12c ] = valid_moves & 0xff;
	ST011_ram[ 0x12d ] = ( valid_moves >> 8 ) & 0xff;

	RunST011 = &ST011_Command;
	ST011_SR = 0xc4;
}


void ST011_Command()
{
#ifdef DEBUG_DSP
	printf( "OP%02X @ line %d\n", ST011_DR, line_count );
#endif

	// busy
	ST011_SR = 0x84;

	switch( ST011_DR )
	{
		// Download shogi playboard to on-board memory
		case 0x01:
			ST011_OP01();
			break;

		// Upload shogi analysis data to outside memory
		case 0x02:
			ST011_input_length = 4;
			RunST011 = ST011_OP02;
			break;

		// Project all moves of player color $40
		case 0x04:
			ST011_OP04();
			break;

		// Project all moves of player color $20
		case 0x05:
			ST011_OP05();
			break;

		// Unknown - seems to set flags $00,$20,$40,..$e0 for restricted movement lists
		case 0x06:
			//ST011_OP06();
			ST011_SR = 0xc4;
			break;

		// Unknown - seems to set flags $00,$20,$40,..$e0 for restricted movement lists
		case 0x07:
			//ST011_OP07();
			ST011_SR = 0xc4;
			break;

		// List valid moves of player color $20
		case 0x0E:
			ST011_OP0E();
			break;

		default:
#ifdef DEBUG_DSP
			printf( "Unknown OP @ line %d\n", line_count );
#endif
			break;
	}
}


unsigned short seta11_address;
unsigned char seta11_byte;

void ST011_MapR_68()
{
  if (seta11_address < 0x1000)
  {
    ST011_DR = ST011_ram[seta11_address & 0xfff];
  }
  seta11_byte = ST011_DR;
}

void ST011_MapW_68()
{
  ST011_DR = seta11_byte;

  if (seta11_address < 0x1000)
  {
    ST011_ram[seta11_address & 0xfff] = ST011_DR;
  }
}

void ST011_MapR_60()
{
  if (seta11_address == 0)
  {
    RunST011();
  }
  if (seta11_address == 1)
  {
    seta11_byte = ST011_SR;
    return;
  }
  seta11_byte = ST011_DR;
}

void ST011_MapW_60()
{
  ST011_DR = seta11_byte;

  if (seta11_address == 0)
  {
    RunST011();
  }
}
